
package org.jetnuke.modules.blog.utils;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URLEncoder;
import java.sql.Connection;
import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.jforum.DBConnection;
import net.jforum.JForumExecutionContext;
import net.jforum.entities.Theme;
import net.jforum.entities.User;
import net.jforum.util.preferences.ConfigKeys;
import net.jforum.util.preferences.SystemGlobals;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.xmlrpc.XmlRpcClient;
import org.jetnuke.common.BlockInfo;
import org.jetnuke.common.IBlockInfo;
import org.jetnuke.dao.BlogCategoryDAO;
import org.jetnuke.dao.BlogHitsDAO;
import org.jetnuke.dao.BlogPostDAO;
import org.jetnuke.dao.BlogUserSettingsDAO;
import org.jetnuke.dao.DataAccessDriver;
import org.jetnuke.dao.PictureUserSettingsDAO;
import org.jetnuke.entities.BlogCategory;
import org.jetnuke.entities.BlogPost;
import org.jetnuke.entities.BlogUserSettings;
import org.jetnuke.entities.PictureUserSettings;
import org.jetnuke.servlets.Do;

public class BlogUtils {
	private final static Logger s_log = Logger.getLogger(BlogUtils.class);
	private BlogUtils(){}
	public static boolean isBlogInitialized(int uid)throws Exception{
		BlogUserSettingsDAO busm=DataAccessDriver.getInst().newBlogUserSettingsDAO();
		BlogUserSettings bus=busm.retrieveByPk(uid);
		if(bus.getID()==0)
			return false;
		return true;
	}
	public static void initializeBlog(int uid)throws Exception{
		BlogCategoryDAO bcm=DataAccessDriver.getInst().newBlogCategoryDAO();
		List ls=bcm.selectByUser(uid);
		if(ls.isEmpty()){
			BlogCategory bc=new BlogCategory();
			bc.setAID(uid);
			bc.setNAME("Uncategorized");
			bc.setDESCRIPTION("Uncategorized Category");
			bc.setPARENTID(-1);
			bcm.addNew(bc);
		}
		BlogUserSettingsDAO busm=DataAccessDriver.getInst().newBlogUserSettingsDAO();
		BlogUserSettings bus=busm.retrieveByPk(uid);
		if(bus.getID()==0){
			bus.setID(uid);
			User user=DataAccessDriver.getInst().newUserDAO().selectById(uid);
			bus.setTITLE(user.getUsername()+"'s Blog");
			bus.setDESCRIPTION("");
			bus.setMAXNUMBERPOST(10);
			int theme=user.getThemeId();
			if(theme==0)
				theme=1;
			bus.setSTYLE(theme);
			busm.insert(bus);
			
		}
	}
	public static void manageStyle(BlogUserSettings bus, VelocityContext vc, List preLeftBlocks){
		String _styleFolder = "_styleFolder";
		String currentStyle=null;
		try {
			Theme tm=net.jforum.dao.DataAccessDriver.getInstance().newThemeDAO().selectByID(bus.getSTYLE());
			currentStyle = tm.getStyleName();
		} catch (Throwable e) {
				
		}
		if(currentStyle!=null)
			vc.put(_styleFolder, "style/" + currentStyle);
		vc.put("_currentStyle",currentStyle);
		vc.put("_preBlocks",preLeftBlocks);
		if(bus.getBLOCKTITLE1()!=null && bus.getBLOCKTITLE1().length()>0 && bus.getBLOCKCONTENT1()!=null && bus.getBLOCKCONTENT1().length()>0){
			preLeftBlocks.add(createBlockInfo(bus.getBLOCKTITLE1(),bus.getBLOCKCONTENT1()));
		}
		if(bus.getBLOCKTITLE2()!=null && bus.getBLOCKTITLE2().length()>0 && bus.getBLOCKCONTENT2()!=null && bus.getBLOCKCONTENT2().length()>0){
			preLeftBlocks.add(createBlockInfo(bus.getBLOCKTITLE2(),bus.getBLOCKCONTENT2()));
		}
		if(bus.getBLOCKTITLE3()!=null && bus.getBLOCKTITLE3().length()>0 && bus.getBLOCKCONTENT3()!=null && bus.getBLOCKCONTENT3().length()>0){
			preLeftBlocks.add(createBlockInfo(bus.getBLOCKTITLE3(),bus.getBLOCKCONTENT3()));
		}
		if(bus.getBLOCKTITLE4()!=null && bus.getBLOCKTITLE4().length()>0 && bus.getBLOCKCONTENT4()!=null && bus.getBLOCKCONTENT4().length()>0){
			preLeftBlocks.add(createBlockInfo(bus.getBLOCKTITLE4(),bus.getBLOCKCONTENT4()));
		}
		if(bus.getBLOCKTITLE5()!=null && bus.getBLOCKTITLE5().length()>0 && bus.getBLOCKCONTENT5()!=null && bus.getBLOCKCONTENT5().length()>0){
			preLeftBlocks.add(createBlockInfo(bus.getBLOCKTITLE5(),bus.getBLOCKCONTENT5()));
		}
	}
	private static IBlockInfo createBlockInfo(String title, String content){
		BlockInfo bInfo=new BlockInfo();
		
		bInfo.setPath("");
		bInfo.setTitle(title);
		bInfo.setAdminOnly(false);
		bInfo.setAnonymousOnly(false);
		bInfo.setBlockClass("org.jetnuke.common.BasicBlock");
		bInfo.setContent(content);
		bInfo.setPosition("l");
		bInfo.setValid(true);
		bInfo.setActivated(true);
		return bInfo;
	}
	public static IBlockInfo createUserBlockInfo(VelocityContext vc, User us){
		StringWriter sw=new StringWriter();
		try {
			PictureUserSettingsDAO pusm=DataAccessDriver.getInst().newPictureUserSettingsDAO();
			PictureUserSettings bus=pusm.retrieveByPk(us.getId());
			if(bus.getID()!=0){
				vc.put("hasPicture",Boolean.TRUE);
			}
			
			
		
			Template t = Velocity.getTemplate("/modules/blog/user_block.vm");
			vc.put("user",us);
			BlogCategoryDAO bcm=DataAccessDriver.getInst().newBlogCategoryDAO();
			List ls=bcm.selectByUser(us.getId());
			vc.put("blogCategories",ls);
			t.merge(vc, sw);
		} catch (Exception e) {
			s_log.error(e);
			
		}
		String title=(String)vc.get("_THIS_BLOG");
		IBlockInfo bInfo=createBlockInfo(title,sw.toString());
		return bInfo;
	}
	public static boolean trackBack(String trackbackURL,String urlEncodedTitle, String urlEncodedExcerpt,String permalink, String urlEncodedBlogName){
		String queryString="title="+urlEncodedTitle+"&url="+permalink+"&blog_name="+urlEncodedBlogName+"&excerpt="+urlEncodedExcerpt;
		
		PostMethod post = new PostMethod(trackbackURL);
		
		
		HttpClient client = new HttpClient();
		try{
			client.setConnectionTimeout(8000);
			String host=post.getURI().getHost();
			post.setRequestHeader("Host", host);
			post.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
			post.setRequestHeader("User-Agent", "JetNuke/"+Do.JETNUKE_VERSION);
			post.setRequestHeader("Content-Length",String.valueOf(queryString.length()));
			post.setRequestBody(queryString);
			

			int returnValue=client.executeMethod(post);
			if(returnValue!=200){
				
				return false;
			}
			BufferedInputStream bis=new BufferedInputStream(post.getResponseBodyAsStream());
			byte[] buff = new byte[2048];
			int bytesRead; 
			ByteArrayOutputStream baos=new ByteArrayOutputStream(2048*20);
						// Simple read/write loop.
			while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
				baos.write(buff,0,bytesRead);
			}
			String response=baos.toString();
			
			
			/*	
			 * From  http://www.sixapart.com/pronet/docs/trackback_spec:
			 * In the event of a succesful ping, the server MUST return a response in the following format:
			 * 
			 * 	<?xml version="1.0" encoding="utf-8"?>
			 	<response>
			 	<error>0</error>
			 	</response>
			 * 
			 * 
			 */
			
			if(response.indexOf("<error>0</error>")>-1){
				return true;
			}
			
		}catch(Exception e){
		}finally{
			post.releaseConnection();
		}
		return false;
	}
	
	public static void doTrackback(String nukeURL,int postID)throws Exception{
		Connection conn = null;
		if (SystemGlobals.getBoolValue(ConfigKeys.BACKGROUND_TASKS)) {
			conn = DBConnection.getImplementation().getConnection();
			JForumExecutionContext ex=new JForumExecutionContext();
			ex.setConnection(conn);
			JForumExecutionContext.set(ex);
			//DataHolder dHolder=new DataHolder();
			//dHolder.setConnection(conn);
			//JForum.setThreadLocalData(dHolder);
		}
		try{
			BlogPostDAO bpm=DataAccessDriver.getInst().newBlogPostDAO();
			BlogPost bp=bpm.retrieveByPK(postID);
			String toPing=bp.getTOPING();
			String[]pings=toPing.split(" ");
			TreeSet pingsSet=new TreeSet(Arrays.asList(pings));
			String []pungs=bp.getPINGED().split(" ");
			TreeSet pungsSet=new TreeSet(Arrays.asList(pungs));
			
			BlogUserSettingsDAO busm=DataAccessDriver.getInst().newBlogUserSettingsDAO();
			BlogUserSettings bus=busm.retrieveByPk(bp.getAID());
			
			String urlEncodedTitle=URLEncoder.encode(bp.getPOSTTITLE(),"UTF-8");
			
			String urlEncodedExcerpt=URLEncoder.encode(bp.getSHORTTEXT(),"UTF-8");
			String permalink=nukeURL+"do/blog/show_page?id="+postID;
			String urlEncodedBlogName=URLEncoder.encode(bus.getTITLE(),"UTF-8");
			Iterator it=pingsSet.iterator();
			while(it.hasNext()){
				String ping=((String)it.next()).trim();
				if(!pungsSet.contains(ping)){
					boolean tracked=trackBack(ping,urlEncodedTitle,urlEncodedExcerpt,permalink,urlEncodedBlogName);
					if(tracked){
						pungsSet.add(ping);
						it.remove();
						
					}
				}
			}
			String[] newPungs=(String[])pungsSet.toArray(new String[pungsSet.size()]);
			String[] newPings=(String[])pingsSet.toArray(new String[pingsSet.size()]);
			StringBuffer sbPing=new StringBuffer("");
			for(int i=0;i<newPings.length;i++){
				sbPing.append(newPings[i].trim());
				if(i<newPings.length-1)
					sbPing.append(" ");
			}
			
			StringBuffer sbPung=new StringBuffer("");
			for(int i=0;i<newPungs.length;i++){
				sbPung.append(newPungs[i].trim());
				if(newPungs[i].trim().length()>0&& i<newPungs.length-1)
					sbPung.append(" ");
			}
			bp.setPINGED(sbPung.toString());
			bp.setTOPING(sbPing.toString());
			bpm.update(bp);
		}finally{
			if (SystemGlobals.getBoolValue(ConfigKeys.BACKGROUND_TASKS)){
				if (conn != null) {
					try {
						DBConnection.getImplementation().releaseConnection(conn);
					}
					catch (Exception e) {}
				}
			}
		}
	}
	 private static final String PINGBACK_METHOD = "pingback.ping";
	 private static final String X_PINGBACK_HEADER = "X-Pingback";
	 private static final String PINGBACK_LINK_REGEX = "<link rel=\"pingback\" href=\"([^\"]+)\" ?/?>";
	 private static final String HREF_REGEX = "href\\s*=\\s*\"(.*?)\"";
	 private static final Pattern pingbackLinkPattern = Pattern.compile(PINGBACK_LINK_REGEX, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.UNICODE_CASE | Pattern.DOTALL);
	 private static final Pattern hrefPattern = Pattern.compile(HREF_REGEX, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.UNICODE_CASE | Pattern.DOTALL);
	 
	public static void doPingback(String url, int id) throws Exception{
		String permalink=url+"do/blog/show_page?id="+id;
		Connection conn = null;
		String lineSeparator=System.getProperty("line.separator");
		
		if (SystemGlobals.getBoolValue(ConfigKeys.BACKGROUND_TASKS)) {
			conn = DBConnection.getImplementation().getConnection();
			JForumExecutionContext ex = JForumExecutionContext.get();
			ex.setConnection(conn);
			JForumExecutionContext.set(ex);
		}
		try{
			BlogPostDAO bpm=DataAccessDriver.getInst().newBlogPostDAO();
			BlogPost bp=bpm.retrieveByPK(id);
			
			
		//	String toPing=bp.getTOPING();
			//String[]pings=toPing.split(" ");
			//TreeSet pingsSet=new TreeSet(Arrays.asList(pings));
			String []pungs=bp.getPINGED().split(" ");
			TreeSet pungsSet=new TreeSet(Arrays.asList(pungs));
			
			
			String text=bp.getPOSTCONTENT();
			
			Matcher hrefMatcher = hrefPattern.matcher(text);
			String targetURI = null;
			TreeSet targetURISet=new TreeSet();
			while (hrefMatcher.find()) {
				targetURI = hrefMatcher.group(1);
				targetURISet.add(targetURI);
			}
			Iterator it=targetURISet.iterator();
			while(it.hasNext()){
				targetURI=(String)it.next();
				if(!targetURI.toLowerCase().startsWith("http"))
					continue;
				if(pungsSet.contains(targetURI))
					continue;
				try {
					
					HttpClient client = new HttpClient();
					GetMethod gm=new GetMethod(targetURI);
					
					gm.setRequestHeader("User-Agent", "JetNuke/"+Do.JETNUKE_VERSION);
					client.setConnectionTimeout(8000);
					client.executeMethod(gm);
					Header hd=gm.getResponseHeader(X_PINGBACK_HEADER);
					String pingbackURL=null;
					if(hd!=null)
						pingbackURL=hd.getValue();
					if (pingbackURL == null) {
						 
						 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gm.getResponseBodyAsStream(), "UTF-8"));
                         StringBuffer content = new StringBuffer();
                         String input;
                         while ((input = bufferedReader.readLine()) != null) {
                             content.append(input).append(lineSeparator);
                         }
                         bufferedReader.close();
                         
                         Matcher pingbackLinkMatcher = pingbackLinkPattern.matcher(content.toString());
                         if (pingbackLinkMatcher.find()) {
                             pingbackURL = pingbackLinkMatcher.group(1);
                         }
					 }
					 if (pingbackURL != null && targetURI != null) {
                         Vector parameters = new Vector();
                         parameters.add(permalink);
                         parameters.add(targetURI);
                         XmlRpcClient xmlRpcClient = new XmlRpcClient(pingbackURL);
                         
                         xmlRpcClient.execute(PINGBACK_METHOD,parameters);
                         pungsSet.add(targetURI);
                         //executeAsync should be better. But it doesn't work on xml-rpc 2.0!!!
                         //It throws a NPE
                         //See http://www.opensubscriber.com/message/xmlrpc-dev@ws.apache.org/1865013.html
                         //System.out.println(obj);
                         
//                        xmlRpcClient.executeAsync(PINGBACK_METHOD,parameters,new AsyncCallback(){
//
//							public void handleResult(Object arg0, URL arg1, String arg2) {
//								System.out.println("ok:"+arg0+" "+arg1.toString()+" "+arg2);
//								
//							}
//
//							public void handleError(Exception arg0, URL arg1, String arg2) {
//								System.out.println("error:"+arg0+" "+arg1.toString()+" "+arg2);
//								
//							}});
                     }
						
				}catch(Throwable t){
					s_log.error(t);
				}
			}
			String[] newPungs=(String[])pungsSet.toArray(new String[pungsSet.size()]);
			StringBuffer sbPung=new StringBuffer("");
			for(int i=0;i<newPungs.length;i++){
				sbPung.append(newPungs[i]);
				if(newPungs[i].trim().length()>0&& i<newPungs.length-1)
					sbPung.append(" ");
			}
			bp.setPINGED(sbPung.toString());
			bpm.update(bp);
		}
		finally{
			if (SystemGlobals.getBoolValue(ConfigKeys.BACKGROUND_TASKS)){
				if (conn != null) {
					try {
						DBConnection.getImplementation().releaseConnection(conn);
					}
					catch (Exception e) {}
				}
			}
		}
		
	}
	public static IBlockInfo createStatInfo(VelocityContext vc) {
		StringWriter sw=new StringWriter();
		try {
			Template t = Velocity.getTemplate("/modules/blog/blog_stat.vm");
			BlogHitsDAO bhm=DataAccessDriver.getInst().newBlogHitsDAO();
			List ls=bhm.selectHotWeeks();
			Iterator it=ls.iterator();
			HashMap mp=new HashMap();
			while(it.hasNext()){
				List intList=(List)it.next();
				//int count=((Integer)intList.get(0)).intValue();
				int author=((Integer)intList.get(1)).intValue();
				BlogUserSettings bus=DataAccessDriver.getInst().newBlogUserSettingsDAO().retrieveByPk(author);
				mp.put(new Integer(author),bus);
			}
			vc.put("mp1",mp);
			vc.put("ls1",ls);
			t.merge(vc, sw);
		} catch (Exception e) {
			s_log.error(e);
		}
		String title=(String)vc.get("_HOT_USER_BLOGS");
		IBlockInfo bInfo=createBlockInfo(title,sw.toString());
		return bInfo;
	}
	public static IBlockInfo createCalendarBlock(VelocityContext vc, GregorianCalendar gc, int uid){
		
		int year=0;
		int month=0;

		
		StringWriter sw=new StringWriter();
		try {
				
			year=gc.get(java.util.Calendar.YEAR);
			month=gc.get(java.util.Calendar.MONTH)+1;//because java months start from 0
			
			Set postDaysSet=DataAccessDriver.getInst().newBlogPostDAO().getPostDays(uid,month,year);
			vc.put("postSet",postDaysSet);
			HashMap days=new HashMap();
			//int subtract=getFirstDayOfWeek();
			Locale locale=(Locale)vc.get("_locale");
			String [] DAYS =	new DateFormatSymbols(locale).getShortWeekdays();
			String [] MONTHS =	new DateFormatSymbols(locale).getMonths();
			String[] dstArray = new String[7];
	        System.arraycopy(DAYS, 1, dstArray, 0, 7);
	        
	        vc.put("current_month",new Integer(month-1));
			vc.put("DAYS",dstArray);
			gc=getFirstDayOnCalendar(year,month-1);
			GregorianCalendar dt=gc;
			for(int i=0;i<6;i++){
				for(int j=0;j<7;j++){
					GregorianCalendar tmp=new GregorianCalendar();
					tmp.setTime(dt.getTime());
					days.put(new Integer(i+j*7),tmp);
					dt.add(java.util.Calendar.DATE,1);
				}
			}
			vc.put("days",days);//Arrays of GregorianCalendar!
			int nextYear=year;
			int previousYear=year;
			int nextMonth=month+1;
			if(nextMonth>12){
				nextMonth=1;
				nextYear+=1;
			}
			int previousMonth=month-1;
			if(previousMonth<1){
				previousYear-=1;
				previousMonth=12;
			}
			vc.put("previousMonth",new Integer(previousMonth));
			vc.put("nextMonth",new Integer(nextMonth));
			vc.put("previousYear",new Integer(previousYear));
			vc.put("nextYear",new Integer(nextYear));
			vc.put("currentMonthDes",MONTHS[month-1]);
			vc.put("year",new Integer(year));
			
			vc.put("calendar_aid",new Integer(uid));
			Template t = Velocity.getTemplate("/modules/blog/calendar.vm");
			t.merge(vc, sw);
		} catch (Exception e) {
			s_log.error(e);
		}
		String title=(String)vc.get("_CALENDAR");
		IBlockInfo bInfo=createBlockInfo(title,sw.toString());
		return bInfo;
	}
	private static GregorianCalendar getFirstDayOnCalendar(int year_, int month_) {

		GregorianCalendar g = new GregorianCalendar(year_, month_, 1);
		int dow = g.get(java.util.Calendar.DAY_OF_WEEK);

		if (dow != 1)
			g.add(java.util.Calendar.DATE, ((dow - 1) * -1));

		return g;
	}
}
